/*
 * 定时器实现
 * timerfd当定时器超时时，通过文件描述符接口进行通知, read返回超时次数; 未超时时，read将阻塞
 * 利用上述功能，用epoll监视定时器fd，发生EPOLLIN事件时，调用定时器超时回调
 *
 * Author: couryzh
 * Email: couryzh@aliyun.com
 *
 */
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include "compile_macro.h"
#include "timer.h"
#include "log.h"


#define MAX_TIMER_LOW_LEVEL   6 // 定时器数量低水位
#define MAX_TIMER_HIGH_LEVEL  40 // 定时器数量高水位

#define MIN_MONITOR_INTERVAL  50 // 最小监测间隔 单位millisecond

typedef struct {
	int in_use;           // 是否使用
	int timer_fd;         // timerfd  启动后，超时发生时可读，详见timerfd_create， timerfd_settime手册
	timer_callback cb;
	void *cb_param;
} timer_task_t;

typedef struct {
	int epoll_fd;
	int monitor_interval;     // 监视间隔，单位millisecond
	pthread_t monitor_thread; // 线程id
	int monitor_quit;         // 线程退出标志
	pthread_mutex_t task_locker; // 保护锁，保证timer_start(), timer_stop()多线程安全
	int num_task;             // 任务数
	timer_task_t *tasks;      // 动态内存
} timer_context_t;

timer_context_t g_timer_ctx;

/* 返回值定义 */
enum {
	TIMER_SUCC = 0,   // 成功
	TIMER_ERR_PARAM,  // 参数错误
	TIMER_ERR_TIMERID, // timeid错误
	TIMER_ERR_NOSPACE, // 没有可用空间
	TIMER_ERR_NOMEM,   // 内存不足
	TIMER_ERR_INTER   // 内部错误
};



/*
 * 获得错误的信息
 */
char *timer_strerror(int err)
{
	switch (err) {
		case TIMER_SUCC:
			return "success";
		case TIMER_ERR_PARAM:
			return "param error";
		case TIMER_ERR_TIMERID:
			return "timer id error";
		case TIMER_ERR_NOSPACE:
			return "no space";
		case TIMER_ERR_NOMEM:
			return "no memory";
		case TIMER_ERR_INTER:
			return "interal error";
		default:
			return "unknown error";
	}
}

/*
 * timerfd 初始化
 * @param repeat 是否重复 0表示一次性定时器
 * @param interval 定时间隔
 * @return 成功 timerfd, 失败返回错误码
 */
int timerfd_init(int *timerfd, int repeat, const struct timeval *interval)
{
	int ret, fd;
	struct timespec now;
	struct itimerspec its;

	// 创建timerfd
	fd = timerfd_create(CLOCK_REALTIME, 0);
	if (fd == -1) {
		log_err("timerfd_create: %s\n", strerror(errno));
		return TIMER_ERR_INTER;
	}

	// 设置时间参数
	// 取当前时间
	if (clock_gettime(CLOCK_REALTIME, &now) == -1) {
		log_err("clock_gettime: %s\n", strerror(errno));
		close(fd);
		return TIMER_ERR_INTER;
	}
	// 初始值需要使用时间绝对值
	its.it_value.tv_sec = now.tv_sec + interval->tv_sec;
	if (now.tv_nsec + interval->tv_usec * 1000 > 999999999) {
		its.it_value.tv_sec += 1;
	}
	its.it_value.tv_nsec = (now.tv_nsec + interval->tv_usec * 1000) % 1000000000;
	// 间隔值为相对值
	if (repeat) {
		its.it_interval.tv_sec = interval->tv_sec;
		its.it_interval.tv_nsec = interval->tv_usec * 1000;
	}
	else { // 一次性定时器，间隔填0
		its.it_interval.tv_sec = 0;
		its.it_interval.tv_nsec = 0;
	}
	log_debug("it_value: %ld.%ld, it_interval: %ld.%ld\n", its.it_value.tv_sec, its.it_value.tv_nsec, its.it_interval.tv_sec, its.it_interval.tv_nsec);
	// 设置timer
	ret = timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, NULL);
	if (ret == -1) {
		log_err("timerfd_settime: %s\n", strerror(errno));
		close(fd);
		return TIMER_ERR_INTER;
	}

	log_debug("open timerfd %d\n", fd);
	*timerfd = fd;
	return TIMER_SUCC;
}

/*
 * timerfd销毁
 * @param fd timerfd
 * @return  错误码
 */
int timerfd_destroy(int fd)
{
	close(fd);
	return TIMER_SUCC;
}


/*
 * 获取timer id
 * @param task  当前timer task地址
 * @return timer id 失败返回-1
 */
int get_timer_id(timer_task_t *task)
{
	if (!task) return -1;

	timer_task_t *init_task = g_timer_ctx.tasks;
	return task - init_task;
}

/*
 * 监视线程入口
 */
void *monitor_all_timer(void *arg)
{
	int i, nf;
	int  _UNUSED ret;
	uint64_t occur = 0; // 超时发生次数
	timer_task_t *task = NULL;
	struct epoll_event ev[MAX_TIMER_HIGH_LEVEL];

	while (!g_timer_ctx.monitor_quit) {
		nf = epoll_wait(g_timer_ctx.epoll_fd, ev, g_timer_ctx.num_task, g_timer_ctx.monitor_interval);
		if (nf == -1) {
			log_err("epoll_wait: %s\n", strerror(errno));
			break;
		}
		else {
			for (i=0; i<nf; i++) {
				if (ev[i].events & EPOLLIN) { // timerfd 超时时，可读
					task = (timer_task_t *)ev[i].data.ptr;
					if (task) {
						ret = read(task->timer_fd, &occur, sizeof(occur));
						log_debug("timer id %d: read() return %d: %lu\n", get_timer_id(task), ret, occur);
						if (task->cb) {
							log_debug("task:%p should run\n", task);
							task->cb(occur, task->cb_param);
						}
					}
				}
			}
		}
	}

	log_debug("quit monitor all timer\n");
	return (void *)0;
}


/*
 * 初始化定时器模块
 * @param millisec  无定时器超时情况下的监听间隔
 * @param max_timers 最大监视定时器数
 * @return 0成功 TIMER_ERR_*
 */
int timer_mod_init(int millisec, int max_timers)
{
	int ret;

	if (max_timers < MAX_TIMER_LOW_LEVEL)
		max_timers = MAX_TIMER_LOW_LEVEL;
	if (max_timers > MAX_TIMER_HIGH_LEVEL)
		max_timers = MAX_TIMER_HIGH_LEVEL;
	g_timer_ctx.epoll_fd = epoll_create(max_timers);
	if (g_timer_ctx.epoll_fd == -1) {
		log_err("epoll_create: %s\n", strerror(errno));
		ret =  TIMER_ERR_INTER;
		goto err_return;
	}

	if (millisec < MIN_MONITOR_INTERVAL)
		millisec = MIN_MONITOR_INTERVAL;
	g_timer_ctx.monitor_interval = millisec;

	g_timer_ctx.tasks = (timer_task_t *)calloc(max_timers, sizeof(timer_task_t));
	if (!g_timer_ctx.tasks) {
		ret = TIMER_ERR_NOMEM;
		goto close_ep_fd;
	}
	g_timer_ctx.num_task = max_timers;

	pthread_mutex_init(&g_timer_ctx.task_locker, NULL);
	ret = pthread_create(&g_timer_ctx.monitor_thread, NULL, monitor_all_timer, NULL);
	if (ret) {
		log_err("create thread failed: %s\n", strerror(ret));
		ret = TIMER_ERR_INTER;
		goto free_task;
	}

	return TIMER_SUCC;

free_task:
	free(g_timer_ctx.tasks);

close_ep_fd:
	close(g_timer_ctx.epoll_fd);

err_return:
	return ret;
}


/*
 * 获取空闲task slot
 * @return 空闲task地址,失败返回NULL
 */
timer_task_t *acquire_task_slot()
{
	int i;
	timer_task_t *tsk, *free_task = NULL;

	pthread_mutex_lock(&g_timer_ctx.task_locker);
	tsk = g_timer_ctx.tasks;
	for (i=0; i<g_timer_ctx.num_task; i++) {
		if (tsk->in_use == 0) {
			tsk->in_use = 1;
			free_task = tsk;
			break;
		}
		tsk++;
	}
	pthread_mutex_unlock(&g_timer_ctx.task_locker);

	return free_task;
}

/*
 * 归还task slot
 * @param task task地址
 */
void release_task_slot(timer_task_t *task)
{
	pthread_mutex_lock(&g_timer_ctx.task_locker);
	memset(task, 0, sizeof(*task));
	pthread_mutex_unlock(&g_timer_ctx.task_locker);
}


/* 
 * 开始一个定时器
 * @param timer_id 成功返回timer_id, 失败置为-1
 * @param repeat 是否重复 0表示只运行一次，超时后不再运行
 * @param interval 超时间隔
 * @param callback 超时回调
 * @param cb_param 回调函数的参数
 * @return 0成功 TIMER_ERR_*
 */
int timer_start(int *timer_id, int repeat, struct timeval interval, timer_callback cb, void *cb_param)
{
	int ret = TIMER_SUCC, timerfd = 0, tid;
	struct epoll_event e;

	*timer_id = -1;
	timer_task_t *task = acquire_task_slot();
	if (task == NULL) {
		log_warn("no free slot, you may change max_timers arg in timer_mod_init()\n");
		return TIMER_ERR_NOSPACE;
	}
	// 计算timer_id
	tid = get_timer_id(task);
	log_debug("timer task %p\n", task);
	log_debug("timer id %d\n", tid);

	ret = timerfd_init(&timerfd, repeat, &interval);
	if (ret) {
		goto release_task;
	}

	// 填充task info
	task->timer_fd = timerfd;
	task->cb = cb;
	task->cb_param = cb_param;

	// 添加到epoll
	e.events = EPOLLIN;
	e.data.ptr = task;
	ret = epoll_ctl(g_timer_ctx.epoll_fd, EPOLL_CTL_ADD, task->timer_fd, &e);
	if (ret == -1) {
		ret = TIMER_ERR_INTER;
		log_err("epoll_ctl ADD: %s\n", strerror(errno));
		goto destroy_timerfd;
	}

	// 计算timer_id
	*timer_id = tid;

	return TIMER_SUCC;

destroy_timerfd:
	timerfd_destroy(task->timer_fd);

release_task:
	release_task_slot(task);
	return ret;
}

/*
 * 停止定时器
 * @param timer_id 待停止的timer_id
 * @return 0成功 TIMER_ERR_*
 */
int timer_stop(int timer_id)
{
	int ret;
	timer_task_t *task = NULL;

	if (timer_id < 0 || timer_id >= g_timer_ctx.num_task)
		return TIMER_ERR_TIMERID;

	task = g_timer_ctx.tasks + timer_id;

	// epoll不再监视timerfd
	if ((ret = epoll_ctl(g_timer_ctx.epoll_fd, EPOLL_CTL_DEL, task->timer_fd, NULL)) == -1) {
		log_err("epoll_ctl DEL: %s\n", strerror(errno));
		return TIMER_ERR_INTER;
	}
	// timerfd销毁
	timerfd_destroy(task->timer_fd);

	// 归还task slot
	release_task_slot(task);
	return TIMER_SUCC;
}

/*
 * 销毁定时器模块
 * @return 0成功 TIMER_ERR_*
 */
int timer_mod_destroy()
{
	int i;
	timer_task_t *task = NULL;

	task = g_timer_ctx.tasks;
	pthread_mutex_lock(&g_timer_ctx.task_locker);
	for (i=0; i<g_timer_ctx.num_task; i++) {
		if (task->in_use) {
			timer_stop(i);
		}
	}
	pthread_mutex_unlock(&g_timer_ctx.task_locker);

	// 等待线程退出
	g_timer_ctx.monitor_quit = 1;
	usleep(g_timer_ctx.monitor_interval * 1000 * 2);

	pthread_mutex_destroy(&g_timer_ctx.task_locker);
	free(g_timer_ctx.tasks);
	close(g_timer_ctx.epoll_fd);

	return TIMER_SUCC;
}

